<template>
  <el-row>
    <el-form :inline="true" :model="form" class="demo-form-inline" ref="form">
      <el-form-item label="行数" required>
        <el-input v-model="form.row" size="mini" min="3" type="number"></el-input>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" size="mini" @click="onSubmit" :disabled="!form.row || !form.row">生成迷宫</el-button>
      </el-form-item>
      <el-form-item>
        <el-button type="warning" size="mini" @click="solve" :disabled="!canGetSolve">解答</el-button>
      </el-form-item>
    </el-form>
    <canvas id="canvas" width="640" height="640" ref="canvas"></canvas>
  </el-row>
</template>
<script>
export default {
  name: 'maze',
  data() {
    return {
      context: {},
      canvas: null,
      maze: [],
      form: {
        row: 20,
        col: 20
      },
      canGetSolve: true
    }
  },
  components: {},
  watch: {},
  computed: {},
  created() {
  },
  mounted() {
    this.onSubmit()
  },
  methods: {
    solve() {
      this.canGetSolve = false
      this.getSolve()
      this.context.clearRect(0, 0, this.canvas.getAttribute("width"), this.canvas.getAttribute("height"))
      this.drawMaze()
    },
    getSolve() {
      let maze = this.maze
      let current = {x: 1, y: 1}
      let visitedList = []
      let result = []
      let out = []
      visitedList.push(current)
      result.push(current)
      while (current.x !== maze.length - 2 || current.y !== maze[0].length - 2) {
        let aroundBlock = this.findAroundBlock(current, maze)
        let toList = aroundBlock.filter(x => !this.arrayContainsObject(visitedList, x))
        if (toList.length === 0) {
          out.push(result.pop())
          current = result[result.length - 1]
        } else {
          current = toList[Math.floor(Math.random() * toList.length)]
          visitedList.push(current)
          result.push(current)
        }
      }
      result.forEach(element => {
        maze[element.x][element.y] = 2
      });
      this.maze = maze
    },
    findAroundBlock(current, tArray) {
      let currentRow = current.x;
      let currentCol = current.y;
      var newToList = [];
      // 左
      if (currentRow - 2 >= 0 && tArray[currentRow - 1][currentCol] === 0) {
        newToList.push({x: currentRow - 1, y: currentCol})
      }
      // 右
      if (currentRow + 2 <= tArray.length - 1 && tArray[currentRow + 1][currentCol] === 0) {
        newToList.push({x: currentRow + 1, y: currentCol})
      }
      // 上
      if (currentCol - 2 >= 0 && tArray[currentRow][currentCol - 1] === 0) {
        newToList.push({x: currentRow, y: currentCol - 1})
      }
      // 下
      if (currentCol + 2 <= tArray[0].length - 1 && tArray[currentRow][currentCol + 1] === 0) {
        newToList.push({x: currentRow, y: currentCol + 1})
      }
      return newToList
    },
    onSubmit() {
      this.form.col = this.form.row
      this.canGetSolve = true
      this.initDraw()
      this.initialMaze(this.form.row, this.form.col)
      this.context.clearRect(0, 0, this.canvas.getAttribute("width"), this.canvas.getAttribute("height"))
      this.drawMaze()
    },
    initDraw() {
      this.canvas = null
      this.context = null
      this.canvas = document.getElementById('canvas')
      this.context = this.canvas.getContext('2d')
      this.context.strokeStyle = '#000000'
      this.context.fillStyle = '#000000'
    },
    initialMaze(row, col) {
      let mazeRow = 2 * row + 1
      let mazeCol = 2 * col + 1
      var tArray = []; //先声明一维
      for (var k = 0; k < mazeRow; k++) { //一维长度为i,i为变量，可以根据实际情况改变
        tArray[k] = []; //声明二维，每一个一维数组里面的一个元素都是一个数组；
        for (var j = 0; j < mazeCol; j++) { //一维数组里面每个元素数组可以包含的数量p，p也是一个变量；
          tArray[k][j] = k % 2 == 0 || j % 2 == 0 ? 1 : 0; //这里将变量初始化，我这边统一初始化为空，后面在用所需的值覆盖里面的值
        }
      }
      var visitedList = [];

      let current = {x: 1, y: 1}
      visitedList.push(current)

      while (visitedList.length < row * col) {
        var list = this.findToVisitedList(current, tArray)
        var newToList = list.filter(x => !this.arrayContainsObject(visitedList, x))
        if (newToList.length !== 0) {
          let next = newToList[Math.floor(Math.random() * newToList.length)]
          this.changeValue(current, next, tArray)
          current = next
          visitedList.push(current)
        } else {
          current = list[Math.floor(Math.random() * list.length)]
        }
      }
      this.maze = tArray
    },
    arrayContainsObject(visitedList, x) {
      for (let index = 0; index < visitedList.length; index++) {
        if (visitedList[index].x === x.x && visitedList[index].y === x.y) {
          return true
        }
      }
      return false
    },
    changeValue(current, next, tArray) {
      let x = (current.x + next.x) / 2
      let y = (current.y + next.y) / 2
      tArray[x][y] = 0
    },
    findToVisitedList(current, tArray) {
      let currentRow = current.x;
      let currentCol = current.y;
      var newToList = [];
      if (currentRow - 2 > 0) {
        newToList.push({x: currentRow - 2, y: currentCol})
      }
      if (currentRow + 2 < tArray.length - 1) {
        newToList.push({x: currentRow + 2, y: currentCol})
      }
      if (currentCol - 2 > 0) {
        newToList.push({x: currentRow, y: currentCol - 2})
      }
      if (currentCol + 2 < tArray[0].length - 1) {
        newToList.push({x: currentRow, y: currentCol + 2})
      }
      return newToList
    },
    drawMaze() {
      let row = this.maze.length
      let col = this.maze[0].length
      let yOffset = this.canvas.getAttribute("height") / row
      let xOffset = this.canvas.getAttribute("width") / col
      let whiteBlocks = []
      let blackBlocks = []
      let redBlocks = []
      for (let indexI = 0; indexI < this.maze.length; indexI++) {
        for (let indexJ = 0; indexJ < this.maze[0].length; indexJ++) {
          let strokeStyle = indexI === 1 && indexJ === 1 ? '#0000FF' : indexI === row - 2 && indexJ === col - 2 ? '#DC143C' : '#F0F8FF'
          let startX = indexI * xOffset
          let startY = indexJ * yOffset
          let endX = indexI * xOffset + xOffset
          let endY = indexJ * yOffset + yOffset
          if (this.maze[indexI][indexJ] === 0) {
            whiteBlocks.push({strokeStyle: strokeStyle, startX: startX, startY: startY, endX: endX, endY: endY})
          } else if (this.maze[indexI][indexJ] === 1) {
            blackBlocks.push({
              strokeStyle: strokeStyle,
              fillStyle: '##000000',
              startX: startX,
              startY: startY,
              endX: endX,
              endY: endY
            })
          } else if (this.maze[indexI][indexJ] === 2) {
            redBlocks.push({
              strokeStyle: strokeStyle,
              fillStyle: '#DC143C',
              startX: startX,
              startY: startY,
              endX: endX,
              endY: endY
            })
          }
        }
      }
      this.drawFillRects(blackBlocks)
      this.drawFillRects(redBlocks)
      this.drawRectangles(whiteBlocks)
    },
    drawFillRects(list) {
      list.forEach(x => {
        this.drawFillRect(x.strokeStyle, x.fillStyle, x.startX, x.startY, x.endX, x.endY)
      })
    },
    drawRectangles(whiteBlocks) {
      whiteBlocks.forEach(x => {
        this.drawRectangle(x.strokeStyle, x.startX, x.startY, x.endX, x.endY)
      })
    },
    drawRectangle(strokeStyle, startX, startY, endX, endY) {
      this.context.strokeStyle = strokeStyle
      this.context.beginPath()
      this.context.strokeRect(startX, startY, (endX - startX), (endY - startY))
      this.context.stroke()
      this.context.closePath()
    },
    drawFillRect(strokeStyle, fillStyle, startX, startY, endX, endY) {
      this.context.strokeStyle = strokeStyle
      this.context.fillStyle = fillStyle
      this.context.beginPath()
      this.context.fillRect(startX, startY, (endX - startX), (endY - startY))
      this.context.stroke()
      this.context.closePath()
    }
  },
}
</script>

<style lang="scss" scoped>
.loading-mask {
  position: relative;
}

.scroll-expand {
  padding-right: 0;
  overflow: initial !important;

  .el-scrollbar__wrap {
    overflow: initial !important;
    overflow-x: hidden;

    .el-scrollbar__view {
      height: 100%;
    }
  }
}

.scroll-collapse {
  padding-right: 15px;

  .el-scrollbar__wrap {
    overflow: inherit !important;

    .el-scrollbar__view {
      height: 100%;
    }
  }

  overflow: initial !important;
}
</style>
